Django

text::python

official::Django

Django前后端分离:text::python

一、准备工作

1 简单流程

# 1.安装
pip install django

# 2.终端中新建项目
cd path
django-admin startproject <project_name>

# 3.新建app(工程主目录下,任一)
cd <project_name>
python manage.py startapp <app_name>
django-admin startapp <app_name>

# 4.注册app(在setting.py修改)
INSTALLED_APPS = [
... ,
<app_name>.apps.<app_name>Config
]
# 案例:app名字是Main
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'Main.apps.MainConfig'
]

# 5.编写urls(详细在urls.py中)
from Main import views
urlpatterns = [
path('index/', views.index)
]

# 6.编写views(详细在views.py中)
from django.shortcuts import render,HttpResponse
def index(request):
return HttpResponse("Hello world")

# 7.Django,启动!(注意路径)
python manage.py runserver [port]

2 默认项目文件

2.1 manage.py

项目管理、启动项目、创建APP、数据管理。

代码不需要修改。但需要经常使用。

2.2 /init.py

>

2.3 asgi.py

异步式接受网络请求。

代码不需要修改。

2.4 wsgi.py

同步式接受网络请求。

代码不需要修改。

2.5 urls.py

路由,URL和views函数的对应关系。

经常修改的文件。

如果导入时爆红,可以按如下方式设置:文件——设置——项目:Web——项目结构——添加内容根——项目的根文件夹(app和默认项目文件的根)

# 添加路径,Main是app,关联url和views
from django.contrib import admin
from django.urls import path

from Main import views

urlpatterns = [
# path('admin/', admin.site.urls),

# 路径:127.0.0.1:8080
path('', views.first)

# 路径:127.0.0.1:8000/index
path('index/', views.index)

# name:给URL命名。这个名称可以在模板、views.py(反向解析URL)等地方使用
path('apple.html', views.index, name='ap')
# templates中html使用如下
<a href="{% url 'ap' %}">

# 带参链接(根据nid值不同生成的链接不同,案例如下。views中的函数要有nid这个参数)
# http://127.0.0.1:8000/depart/2/edit
# http://127.0.0.1:8000/depart/6/edit
path('depart/<int:nid>/edit/', view.depart_edit)
]

2.6 setting.py

项目配置。

经常修改的文件。

# 注册APP
INSTALLED_APPS = [
... ,
'Main.apps.MainConfig'
]

# 模板配置(在根目录下配置一个templates文件,一般不需要,若存在建议删除)
import os
TEMPLATES = [
'DIRS': [os.path.join(BASE_DIR,'templates')],
]

# 静态文件路径
STATIC_URL = 'static/'

# 数据库(推荐改成mysql)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbname',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}

# 错误信息显示(默认:en-us)
LANGUAGE_CODE = "zh-hans"

3 APP

3.1 app定义

项目中对功能进行分块管理。

例如:

  • app,用户管理。【表结构、函数、HTML、CSS】
  • app,订单管理。【表结构、函数、HTML、CSS】
  • app,后台管理。【表结构、函数、HTML、CSS】
# 创建app(工程主目录下,任一)
python manage.py startapp <app_name>
django-admin startapp <app_name>

3.2 Migrations/init.py

当数据库内容进行修改时进行记录。

固定,不用修改。

3.3 admin.py

django默认提供了admin后台管理。

固定,不用修改。

3.4 apps.py

app的启动类

固定,不用修改。

from django.apps import AppConfig

class MainpageConfig(AppConfig):
# 创建数据库默认增加一条bigint的id自增数据
default_auto_field = 'django.db.models.BigAutoField'
# app名称
name = 'mainPage'

3.5 models.py

对数据库进行操作。

经常修改。

执行命令前需要注册app。

Django4对MySQL数据库版本要求是要大于8。

下面试简单案例,详细内容在ORM框架中。

from django.db import models

# 创建表(类一定要继承models.Model)
class UserInfo(models.Model):
name = models.CharField(max_length = 32)
password = models.CharField(max_length = 64)
age = models.IntegerField()

3.6 tests.py

单元测试。

固定,不用修改。

3.7 views.py

视图函数。

经常修改。

定义的函数要带有request参数:

  • request是一个对象,封装了用户发送过来的所有数据。

templates文件夹:

  • 若要在函数中返回模板文件,则在对应view的app下添加templates文件夹,然后在文件夹中写入html文件。
  • 框架在寻找templates时,会根据app的注册顺序,逐一的去寻找他们的templates目录。
  • 如果在setting的TEMPLATES中配置了DIRS,则会最先找DIRS中的路径,再按app的注册顺序找。

static文件夹:

  • 用来存放css、js、img、plugins等静态文件,然后在html中导入是路径改成/static/css/...。(注意最前面有一个斜杠)
  • 导入方式可以写成上述的绝对路径,但是不建议。建议在html头部加上{% load static %},然后按{% static 'css/...' %}方式导入。
# 简单案例(在urls绑定好关系即可访问)
from django.shortcuts import render,HttpResponse

# Create your views here.

# 写的函数必须有request参数
def index(request):
return HttpResponse("Hello world")

# 返回templates中的页面
def user_list(request):
return render(request, "user_list.html")

# 带参,可以给urls传递参数
# http://127.0.0.1:8000/depart/2/edit
# http://127.0.0.1:8000/depart/6/edit
def depart_edit(request, nid):
return render(request, 'depart_edit.html')
<!--导入静态文件方法1-->
<link rel="stylesheet" type="text/css" href="/static/css/main.css">

<!--导入静态文件方法2-->
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">

4 系统类

# 将指定语句标记为安全(这样若语句是html标签语句将会直接执行)
from django.utils.safestring import mark_safe

page_str_list = []
for i in range(1, 21):
ele = "<li><a href = '?page = {}'>{}</a></li>".format(i, i)
page_str_list.append(ele)
page_string = mark_safe("".join(page_str_list))

二、数据处理

1 向浏览器发送参数

写在views的函数中。

想在html中显示参数值时,可以在html中写上{{para}},然后在python函数中传入字典即可。

<!--普通取值-->
<div> n1的值是{{n1}} </div>
<div> n2的值是{{n2}} </div>

<!--列表取值-->
<div> n2的第1个值是{{n2.0}} </div>
<div> n2的第2个值是{{n2.1}} </div>
<div> n2的第3个值是{{n2.2}} </div>

<!--字典取值-->
<div> n3的第name个值是{{n3.name}} </div>
<div> n3的第height个值是{{n3.height}} </div>
<div> n3的第weight个值是{{n3.weight}} </div>
def index(request):
n1 = "Hello World"
n2 = ["admin","user","people"]
n3 = {"name":"Tsumugi","height":152,"weight":38}
params = {"n1": n1, "n2": n2, "n3": n3}
return render(request, "index.html", params)

2 判断

一个块内可以写多行语句。

{% if n3.name == "Tsumugi" %}
<span>Tsumugi</span>
<span>Tsumugi</span>
{% elif n3.height == 160 %}
<span>Height is 160</span>
{% else %}
<span>other</span>
{% endif %}

3 循环

Django不支持enumerate函数。

# 依次获得列表值(forloop.counter表示当前循环的数是第几个(从1开始))
{% for val in n2 %}
<span>n2的第{{ forloop.counter }}个值是{{ val }}<br /></span>
{% endfor %}

# 依次获得键、值、键值(都不加括号)
{% for k in n3.keys %}
<span>keys is {{k}}</span>
{% endfor %}

{% for v in n3.values %}
<span>values is {{v}}</span>
{% endfor %}

{% for k,v in n3.items %}
<li>n3的{{k}}是{{v}}</li>
{% endfor %}

4 获取用户参数

对views函数中的request进行操作。

request是一个对象,封装了用户发送过来的所有数据。

重定向模式:类似DNS的迭代查询,返回网址让浏览器去查询,而不是服务器自己去查询。(可以节约性能)

403 Forbidden:Django自带CSRF漏洞防护,若不带token会报403。所以只需要再form后面加上{% csrf_token %}带上CSRF的token即可解决该问题。

<!--带上CSRF的token-->
<form method="post" action="after_login.html">
{% csrf_token %}
<input type="text" name="user" placeholder="username"/>
<input type="password" name="pwd" placeholder="password"/>
<input type="submit" value="submit" />
</form>
# 定义带有request参数
def test(request):
# 返回内容
return HttpRespoense("Content")
# 返回HTML渲染后的结果
return render(request, "index.html")
# 重定向(要导入redirect)
return redirect("https://www.baidu.com")

# 1.获取请求方式
request.method

# 2.获取请求值(返回QueryDict对象)
QueryDict = request.GET
QueryDict = request.POST
# 获得单个数据(存在获得,不存在返回空)
username = request.POST.get('username', '')

# get获取全部数据(返回字典,数据是字符串)
dic = request.query_params

# post获取全部数据(返回字典,数据是对应的类型)
dic = request.data
# Get重定向
from django.shortcuts import redirect
from django.http import QueryDict

def my_view(request):
params = {'name': 'John', 'age': 25}
query_string = QueryDict('', mutable=True)
query_string.update(params)
return redirect('/new-url/?{}'.format(query_string.urlencode()))

# Post重定向
from django.shortcuts import redirect
from django.http import QueryDict

def my_view(request):
params = {'name': 'John', 'age': 25}
query_string = QueryDict('', mutable=True)
query_string.update(params)

redirect_url = '/new-url/'
redirect_request = request.POST.copy() # 复制原始POST请求数据
redirect_request.update(query_string) # 将参数和POST请求数据合并

return redirect(redirect_url, request=redirect_request)

5 pymysql

-- 创建数据库
create database db DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
import pymysql

# 1.连接MysQL
conn = pymysql.connect ( host="127.0.0.1",port = 3306,user = 'root',passwd = "root123", charset = 'utf8 ', db = 'unicom')

# 2.设置游标
cursor = conn.cursor (cursor = pymysql.cursors.DictCursor)

# 3.操作数据库
cursor.execute( "insert into admin (username,password,mobile) values ( 'admin' , '123456','11011101110')")

# 4.提交数据
conn.commit()

# 5.操作数据库数据


# 6.关闭
cursor.close()
conn.close()

6 ORM框架

因为通过pymysql操作数据库还是太繁琐了,于是Django直接集成了一个ORM框架。底层可以是任意的数据库,类似于PHP的PDO。

代码都是写在models.py中。

使用前要:pip install mysqlclient

ORM可以做的事:

  • 创建、修改、删除数据库中的表。(不用写SQL)
  • 操作表中的数据。(不用写SQL)

ORM不可以做的事:

  • 创建数据库。

设置数据库

# 创建
# 创建字符型(一定要带最大长度)
models.CharField(max_length = 32)

# 创建整形
models.IntegerField()

# 创建小数(总长度10,小数位2)
models.DecimalField(max_digits = 10, decimal_places = 2)

# 创建时间类型
models.DateTimeField() # 年月日时分秒
models.DateField() # 年月日

# 创建自动增加类型
models.BigAutoField(primary_key = True) # bigint
models.AutoField(primary_key = True) # int


# 通用参数
# 备注名称
models.IntegerField(verbose_name = '用户名')

# 配置默认值
models.IntegerField(default = 2)

# 设置可以为空(null:可以是空值、blank:可以无值)
models.IntegerField(null = True, blank = True)

# 设置主键
models.IntegerField(primary_key = True)


# 设置外键(to:关联的表、to_field:关联的列、on_delete:别的表主键被删除后做的处理)
# 注:此处Django会将列名自动改成user_id而非user。
# on_delete参数:models.CASCADE(级联删除,直接删除对应行的数据)、models.SET_NULL(为空,需要设置可以为空)
user = models.ForeignKey(to = "userTable", to_field = "id",on_delete = models.CASCADE)
user = models.ForeignKey(to = "userTable", to_field = "id", null = True, blank = True, on_delete = models.SET_NULL)
# 若字段设置了外键,在views.py中可以通过"字段.外表字段"让Django自动联表获得属性。只是获得外键值则是"字段_id"
queryset = models.UserInfo.objects.all()
for obj in queryset:
obj.depart_id # 获取数据库中存储的那个字段值
obj.deaprt.title # 根据id自动去关联的表中获取那一行数据depart对象。


# 在Django中做的约束
gender_choices = ((1, "男"),(2, "女"))
gender = models.SmallIntegerField(verbose_name = "性别", choices = gender_choices)
# 若一个字段有choices属性,在views.py中可以通过"get_字段名称_display()"让Django自动转化值
queryset = models.UserInfo.objects.all()
for obj in queryset:
obj.gender # 1/2
obj.get_gender_display() # 男/女

数据操作

from django.db import models

# 创建表(类一定要继承models.Model)
class UserInfo(models.Model):
name = models.CharField(max_length = 32)
user = models.ForeignKey(to = "userTable", to_filed = "id",on_delete = models.CASCADE)
"""
上述语句会通过Django自动翻译成下列sql
表名是app名字 + 下划线 + 类名
id是自动加上的且是自增和主键(设置了主键则不会有id)
create table appName_userinfo
(
id bigint auto_increment primary key,
name varchar(32),
password varchar(64),
age int
)
"""


# 删除表字段(直接注释或修改,然后用命令迁移)
class UserInfo(models.Model):
name = models.CharField(max_length = 32)
password = models.CharField(max_length = 64)
# age = models.IntegerField()

# 增加表字段(不能直接增加,使用makemigrations命令后会给出两个选项)
# 1.直接给一个默认值
# 2.退出,在新加字段后面加上默认值或空值。
class UserInfo(models.Model):
name = models.CharField(max_length = 32)
password = models.CharField(max_length = 64)
# age = models.IntegerField()
size = models.IntegerField(default = 2)


# 添加数据:<class>.objects.create(<table_name>=value[,<table_name>=value...])
# 内部会自己转化成insert的sql语句
UserInfo.objects.create(name = "tsumugi", password = "123456", size = "36")

# 删除数据
UserInfo.objects.filter(id = 3).delete() # 选择
UserInfo.objects.all().delete() # 全部

# 更新数据
UserInfo.objects.filter(id = 2).update(password = "999") # 选择
UserInfo.objects.all().update(password = "999") # 全部

# 获取数据
data_list = UserInfo.objects.filter(id = 1) # 选择
data_list = UserInfo.objects.all() # 全部
row_list = UserInfo.objects.filter(id = 1).first() # 获取第一条数据

# 获取每一行数据(用点访问)
for obj in data_list:
print(obj.id, obj.name, obj.password)

# 数据排序
queryset = models.UserInfo.objects.all().order_by("name") # 升序
queryset = models.UserInfo.objects.all().order_by("-name") # 降序

# 数据是否存在
exists = models.UserInfo.objects.filter(mobile=mobile).exists()

# 排除某个值
queryset = models.UserInfo.objects.exclude(id=id).filter(mobile=mobile)

筛选数据

# 筛选数据
# 方法一:直接赋值
UserInfo.objects.filter(mobile="11111111111",id=1)
# 方法二:字典解包
data_dict = {"mobile":"11111111111", "id":1}
UserInfo.objects.filter(**data_dict)


# Django自带数据判断的函数参数(通过:字段__条件 = value设置)
# 数字条件
UserInfo.objects.filter(id=12) # 等于12
UserInfo.objects.filter(id__gt=12) # 大于12
UserInfo.objects.filter(id__gte=12) # 大于等于12
UserInfo.objects.filter(id__lt=12) # 小于12
UserInfo.objects.filter(id__lte=12) # 小于等于12

# 字符串条件
UserInfo.objects.filter(mobile__startswith="1999") # 以1999开头
UserInfo.objects.filter(mobile__endswith="999") # 以999结尾
UserInfo.objects.filter(mobile__contains="995") # 包含995

数据库命令

# 使用下列两个命令完成表的迁移
# 创建数据库迁移文件,自动检测models.py文件中的更改
python manage.py makemigrations
# 执行数据库迁移(首次迁移会迁移多个表,是系统自带的,可以在setting的INSTALLED_APPS中修改)
python manage.py migrate

# 清除makemigrations
python manage.py makemigrations --empty <app_name>

7 模版数据处理

模版内使用函数不要带括号。

模版内不能使用带参数的函数。

<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.name }}</td>
<td>{{ obj.account }}</td>
<!-- 时间处理不能用函数,用管道符和Django内置函数 -->
<td>{{ obj.create_time|date: "Y-m-d H:i:s" }}</td>
<!-- 函数不要加括号,需要时Django会自己加 -->
<td>{{ obj.get_gender_display }}</td>
<td>{{ obj.deaprt.title H</td>
</tr>

8 Redis

official::Django 缓存框架

在Django中可以通过以下方式使用Redis数据库:

  1. 安装redis包:pip install redispip install django-redis
  2. 配置Django的settings.py文件,添加Redis作为缓存后端:
# setting.py中添加

# 添加缓存
CACHES = {
# 设置数据库名
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 网址,可以是多个( username和password改成对应的,没有直接删掉,/0表示是第0个库,可以换)
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",
}
}
}

# 添加session存储位置
SESSION_ENGING = 'django.contrib.sessions.backend.cache'
# 指定使用的session存储的数据库
SESSION_CACHE_ALIAS = 'default'
# 设置session的过期时间(秒)
SESSION_COOKIE_AGE = 60 * 24
# 导入Django Redis模块以获取Redis连接
from django_redis import get_redis_connection

# 获取指定名为'database_name'的Redis连接(通常'Default'是配置中的默认键名)
conn = get_redis_connection('database_name')

# 使用Redis连接设置键值对
conn.set('key', 'value')

# 从Redis中获取之前设置的键对应的值
value = conn.get('key')

# 注意:Redis是一个内存数据库,数据是持久化的,但不是通过调用save()方法来保存更改。
# Redis提供了RDB和AOF两种机制自动或手动将数据同步到磁盘,因此在Python客户端API中不需要手动调用类似`conn.save()`的方法。

# 若要删除键:
conn.delete('key')

# 若要检查键是否存在:
key_exists = conn.exists('key')

# 若要设置过期时间(例如,10秒后过期):
conn.expire('key', 10)

# 若要获取所有键:
keys_list = conn.keys('*')

9 MongoDB

在Django中可以通过以下方式使用Redis数据库:

  1. 安装redis包:pip install mongoengine
  2. 配置Django的settings.py文件,添加Mongo数据库
# setting.py

DATABASES = {
'default': {
...
},
'mongodb': {
'NAME': 'db',
'HOST': '127.0.0.1',
'PORT': 27017,
}
}
# models.py

import mongoengine
from management.settings import DATABASES

mongoengine.connect(DATABASES['mongodb']['NAME'])


class Trends(mongoengine.Document):
tid = mongoengine.StringField() # 编号
# 操作数据库
# 导入MongoDB的Python驱动
from pymongo import MongoClient

# 创建MongoDB客户端连接
client = MongoClient('mongodb://localhost:27017/')

# 连接到指定的数据库
db = client['database_name']

# 操作集合(相当于关系型数据库中的表)
collection = db['collection_name']

# 插入文档
document = {"name": "John", "age": 30}
result = collection.insert_one(document)
print(result.inserted_id)

# 查询文档
query = {"name": "John"}
results = collection.find(query)
for result in results:
print(result)

# 更新文档
query = {"name": "John"}
new_values = {"$set": {"age": 31}}
collection.update_one(query, new_values)

# 删除文档
query = {"name": "John"}
collection.delete_one(query)

三、文件设计

1 模板继承

在父html中写上{% block ... %}{% endblock %},然后在子页面中先{% extends 'father.html' %}引入父页面,再重写{% block ... %}{% endblock %}完成子页面的独立设计。

<!-- 父html:写上block块让子html继承-->
{% load static %}

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Title</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
{% block css %}
{% endblock %}
</head>
<body>
<div class="container">
{% block content %}
{% endblock %}
</div>
<script src="{% static 'js/main.js' %}"></script>
{% block js %}
{% endblock %}
</body>
</html>

<!-- 子html:先引入父html,再书写block即可-->
{% extends 'father.html' %}

{% block css %}
<link rel="stylesheet" type="text/css" href="{% static 'css/other.css' %}">
{% endblock %}

{% block content %}
<h1>Hello World</h1>
{% endblock%}

{% block js %}
<script src="{% static 'js/other.js' %}"></script>
{% endblock %}

2 获得数据库固定数据

# urls.py
urlpatterns = [
path('userAdd.html', view.user_add)
]

# models.py
class UserInfo(models.Model):
name = models.CharField(max_length = 32)
gender_choices = ((1, "男"),(2, "女"))
gender = models.SmallIntegerField(verbose_name = "性别", choices = gender_choices)
depart = models.ForeignKey(to = "Department", to_filed = "id",on_delete = models.CASCADE)

class Department(models.Model):

# views.py
def user_add(request):
context = {
"gender_choices": models.UserInfo.gender_choices,
"depart_list": models.Department.objects.all()
}
return render(request, 'user_add.html', context)

# templates的html
<div>
<label>性别</label>
<select class="form-control">
{% for item in gender_choices %}
<option value="{{ item.0 }}">{{ item.1 }}</option>
{% endfor%}
</select>
</div>
<div class="form-group">
<label>部门</label>
<select class="form-control">
{% for item in depart_list %}
<option value="{{ item.id }}">{{ item.title }}</option>
{% endfor %}
</select>
</div>

3 修改文本

页面没变,通过GET和POST请求的方式不同完成编辑。

# urls.py
urlpatterns = [
path('depart/<int:nid>/list/', view.depart_list),
path('depart/<int:nid>/edit/', view.depart_edit)
]


# views.py
# 根据请求来判断页面
def depart_list(request):
queryset = models.Department.objects.all() # 获取所有部门对象,传递给模板
return render(request, 'depart_edit.html', {"queryset": queryset})

def depart_edit(request, nid):
if request.method == "GET":
# 根据nid,获取数据
row_object = models.Department.objects.filter(id = nid).first()
return render(request, 'depart_edit.html', {"row_object": row_object})

# POST,根据ID进行更新
title = request.POST.get("title")
models.Department.objects.filter(id = nid).update(title = title)
return redirect('/depart/list') # 更新成功后重定向到部门列表页面


# templates的html
<tbody>
{% for obj in queryset %}
<!-- GET请求,进入对应编辑界面 -->
<a href="/depart/{{ obj.id }}/edit/">编辑</a>
{% endfor%}
</tbody>
<div>
<form method="post">
{% csrf_token %}
<input name="title" value="{{ row_object.title }}" />
<button type="submit">提交</button>
</form>
</div>

4 设计时的问题

出现的问题:

  1. 用户提交数据没有校验。
  2. 出现错误时,页面上应该有错误提示。
  3. 页面上,每一个字段都需要我们重新写一遍。
  4. 关联的数据,手动去获取并展示循环展示在页面。

Form组件可以解决前3个问题。ModelForm可以解决全部。


四、Form

1 简单案例

# views.py
from django import forms

# 继承Form
class MyForm(forms.Form):
# widget是设置字段在html中输出时,自动输出为对应input标签
user = forms.CharField(widget=forms.TextInput)
pwd = forms.CharField(widget=forms.PasswordInput)
email = forms.CharField(widget=forms.EmailInput)

def index(request):
if request.method == "GET":
forms = MyForm()
return render(request, 'index.html', {"forms": forms})


# index.html
# 生成的样式:
# <input type="text" name="user" required="" id="id_user">
# <input type="password" name="pwd" required="" id="id_pwd">
# <input type="email" name="email" required="" id="id_email">
<form method="post">
{% for field in forms %}
{{ field }}
{% endfor %}
</form>

五、ModelForm

1 简单案例

# views.py
from django import forms

class MyForm (ModelForm) :
xx = forms.CharField(widget=forms.TextInput)
class Meta:
# 选择数据库
model = UserInfo
# 选择数据库中的值,也可以在此处自定义
fields =["username","password","email","xx"]

def debug():
print(self.instance.pk) # 当前操作的数据的ID

def index(request) :
if request.method == "GET":
forms = MyForm()
return render(request, 'index.html', {"forms": forms})


# index.html
# 生成的样式同理Form
<form method="post">
{% for field in forms %}
{{ field.label }} : {{ field }}
{% endfor %}
</form>

2 模版输出

<!-- field.label获得字段的名称(verbose_name) -->
<form method="post">
{% for field in forms %}
{{ field.label }} : {{ field }}
{% endfor %}
</form>

3 自定义input

# Models.py,连接外表可以在外表对应的数据库中定义__str__方法返回对应的名称,主表生成的下拉框会自动替换id为对应名称
class WorkInfo(models.Model):
""" 作品表 """
title = models.CharField(verbose_name='作品分区', max_length=32)

def __str__(self):
return self.title

# views.py,给input加上对应类名
class UserHodelForm(forms.ModelForm):
class Meta:
model = models.UserInfo
fields = ["name","password","age"]
# 手动给每一个input绑定class值或其他值
widgets = {
"name": forms.TextInput(attrs={"class": "form-control"}),
"password": forms.PasswordInput(attrs={"class": "form-control"}),
"age": forms.TextInput(attrs={"class": "form-control"}),
}
# 循环的方式绑定
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for name,field in self.fields.items():
field.widget.attrs = {"class": "form-control"}

4 数据操作

# views.py
class UserHodelForm(forms.ModelForm):
# 重写验证条件,默认只有非空验证(validators:正则表达式)
name = forms.CharField(label="用户名", min_length = 3)
password = forms.CharField(label="密码", validators = [RegexValidator(r'^1[3-9]\d{9}$', '密码格式错误')])
class Meta:
model = models.UserInfo
fields = ["name","password","age"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for name,field in self.fields.items():
field.widget.attrs = {"class": "form-control"}

def user_model_form_add(request):
""" 添加数据 """
# 自定的ModelForm类。model设置了哪个数据库就会对哪个数据库做操作
form = UserModelForm(data = request.POST)
# 验证数据合法
if form.is_valid():
# 保存数据到数据库,数据库是model指向的数据库
form.save()
return redirect('index.html')
else:
# 存在错误信息form的值会被改变
return render(request, 'index.html', {"form": form})

def user_edit(request, nid):
"""编辑用户"""
# 根据ID去数据库获取要编辑的那一行数据(对象)
row_object = models.UserInfo.objects.filter(id = nid).first()
# Get请求进行
if request.method == "GET":
# 加上instance = row_object后数据会直接显示在界面上充当默认数据
form = UserModelForm(instance = row_object)

return render(request,'index.html',{'form': form})

# 告知操作的对象
form = UserModelForm(data = request.POST, instance = row_object)
if form.is_valid():
# 默认保存的是用户输入的所有数据,如果想要再用户输入以外增加值可以输入下列代码
# form.instance.字段名=值
form.save()
return redirect('index.html')
else:
return render(request, 'index.html', {"form": form})

# index.html
<form method="post">
{% for field in form %}
<label> {{ field.label }} </label>
{{ field }}
<!-- 输出错误信息,可能非常多,是一个列表,只用第一个即可 -->
<span style="color: red"> {{ field.errors.0 }} </style>
{% endfor %}
</form>

5 Meta类

# 排除某个字段(例如:level)
class Meta:
model = models.UserInfo
fields = "__all__"
exclude = ['level']

6 数据校验

class PrettyModelForm(forms.ModelForm):
# 方式1:正则表达式
mobile = forms.CharField(
label = "手机号"
validators = [RegexValidator(r'^1[3-9]\d9$', '手机号格式错误")],

# 方式2:钩子函数(若字段存在,类会自动生成clean_字段的函数,在接收到数据后会自动调用该函数)
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
if len(txt_mobile) !=11:
# 验证不通过
raise ValidationError("格式错误")
# 验证通过,用户输入的值返回
return txt_mobild

六、网络

1 持久化

# 设置session
request.session['info'] = {'id': admin_object.id, 'name': admin_object.username}

2 跨域问题

同源策略是浏览器的一项安全策略,浏览器只允许js代码请求和当前所在服务器域名,端口,协议相同的数据接口上的数据,这就是同源策略。

也就是说,当协议、域名、端口任意一个不相同时,都会产生跨域问题,所以又应该如何解决跨域问题呢。

安装:

  • pip install djangorestframework
  • pip install django-cors-headers
# setting.py配置
# 修改代码
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
]

# 注:引入在第三行
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
...
]


# 添加代码
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_HEADERS = ['*']

# CSRF跨域
CSRF_TRUSTED_ORIGINS = ['127.0.0.1:5173']

七、好用组件

在对应app下新建一个utils文件夹,然后文件夹下放组件

1 分页组件

"""
白定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:
在视图函数中:
def pretty_list(request):
# 1.根据自己的情况去筛选自己的数据
queryset = models.PrettuNum.objects.all()
# 2.实例化分页对象
page_object = Pagination(request, queryset)
context = {
"queryset": page_object.page_queryset, # 分完页的数据
"page_string": page_object.html() # 生成页码
}
return render(request,'pretty_list.html' , context)

在HTML页面中:
{% for obj in queryset %}
{{obj.xx}}
{% endfor %}

<ul class="pagination">
{{ page_string }}
</ul>
"""

from django.utils.safestring import mark_safe

class Pagination(object):
def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):
"""
:param request: 请求的对象
:param queryset: 符合条件的数据(根据这个数据给他进行分页处理)
:param page_param: 每页显示多少条数据
:param page_param: 在URL中传递的获取分页的参数,例如: /list/?page=12
:param plus: 显示当前页的前或后几页(页码)
"""
page = request.GET.get(page_param, "1")
if page.isdecimal():
page = int(page)
else:
page = 1
self.page = page
self.page_size = page_size

self.start = (page - 1) * page_size
self.end = page * page_size

self.page_queryset = queryset[self.start: self.end]

total_count = queryset.count()
total_page_count, div = divmod(total_count, page_size)
if div:
total_page_count += 1
self.total_page_count = total_page_count
self.plus = plus

def html(self):
# 计算出,显示当前页的前5页、后5页
if self.total_page_count <= 2 * self.plus + 1:
# 数据库中的数据比较少,都没有达到11页。
start_page = 1
end_page = self.total_page_count

else:
# 数据库中的数据比较多 > 11页。
# 当前页<5时(小极值)
if self.page <= self.plus:
start_page = 1
end_page = 2 * self.plus + 1
else:
# 当前页 > 5
# 当前页 + 5 > 总页面
if (self.page + self.plus) > self.total_page_count:
start_page = self.total_page_count - 2 * self.plus
end_page = self.total_page_count
else:
start_page = self.page - self.plus
end_page = self.page + self.plus
# 页码
page_str_list = []
page_str_list.append('<li><a href="?page={}">首页</a></li> '.format(1))
# 上一页
if self.page > 1:
prev = '<li><a href="?page={}">上一页</a></li>'.format(self.page - 1)
else:
prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
page_str_list.append(prev)

# 页面
for i in range(start_page, end_page + 1):
if i == self.page:
ele = '<li class="active"><a href="?page=P{}">{}</a></li>'.format(i, i)
else:
ele = '<li><a href=" ?page={}">{}</a></li> '.format(i, i)
page_str_list.append(ele)
# 下一页
if self.page < self.total_page_count:
prev = '<li><a href="?page={}">下一页</a></li>'.format(self.page + 1)
else:
prev = '<li><a href="?page={}">下一页</a></li> '.format(self.total_page_count)
page_str_list.append(prev)

# 尾页
page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(self.total_page_count))
search_string = """

"""
page_str_list.append(search_string)
page_string = mark_safe("".join(page_str_list))
return page_string

2 邮箱验证码

流程:

  1. 注册163.com邮箱

  2. 在设置中开启IMAP/SMTP和POP3/SMTP服务(授权密码要记好,只会显示一次)

  3. 配置setting.py
  4. 发送

official::发送邮件

# setting.py中添加
# 发送邮件
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
# 发送邮件的邮箱
EMAIL_HOST_USER = 'xxx@163.com'
# 在邮箱中设置的授权密码
EMAIL_HOST_PASSWORD = ''
# 收件人看到的发件人
EMAIL_FROM = 'name <xxx163.com>'
def send_captcha_email(email, captcha):
subject = '验证码' # 邮件主题
message = f'您的验证码是: {captcha}' # 邮件内容

# 发送邮件
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [email])